package users

import (
	"context"
	"database/sql"
	"errors"
	"github.com/zeromicro/go-zero/core/logx"
	"github.com/zeromicro/go-zero/core/stores/sqlx"
	"product/authorization/model/tenants"
	"product/authorization/model/users"
	"product/authorization/serve/internal/svc"
	"product/authorization/serve/internal/types"
	"product/common/datetime"
	"product/common/encryption"
	"product/common/enumeration"
	"product/common/toolbox"
	"product/common/validator"
	"time"
)

type AddUsersLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

// NewAddUsersLogic add users
func NewAddUsersLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AddUsersLogic {
	return &AddUsersLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *AddUsersLogic) AddUsers(req *types.AddUserReq) (resp *types.AddUserResp, err error) {
	// 验证用户输入合法性
	validFlag, err := validInputUser(req)
	if !validFlag && err != nil {
		return &types.AddUserResp{Message: "添加失败: " + err.Error(), Ok: false},
			nil
	}

	var tenant *tenants.Tenants
	if !validator.IsEmptyString(req.TenantName) && !validator.IsEmptyString(req.TenantDomain) {
		tenant = mappingTenant(req)
	}

	user := mappingUser(req)
	// 保存,且必须保证用户名唯一
	err = l.svcCtx.UsersModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
		existName, err := l.svcCtx.UsersModel.CheckUserNameExists(ctx, req.UserName)
		if err == nil && existName {
			return errors.New("用户名已存在")
		}

		existMobile, err := l.svcCtx.UsersModel.CheckPhoneExists(ctx, req.Mobile)
		if err == nil && existMobile {
			return errors.New("手机号已存在")
		}

		existEMail, err := l.svcCtx.UsersModel.CheckEmailExists(ctx, req.Email)
		if err == nil && existEMail {
			return errors.New("邮箱已存在")
		}

		// 若是租户不为空的话,创建租户
		if tenant != nil {
			// 创建租户
			id, err := l.svcCtx.TenantModel.SaveTenant(l.ctx, tenant)
			if err != nil {
				return err
			}
			// 绑定租户
			user.TenantID = sql.NullInt64{Int64: id, Valid: true}
		}
		_, err = l.svcCtx.UsersModel.Insert(ctx, user)
		return err
	})
	if err != nil {
		return &types.AddUserResp{Message: "注册失败: " + err.Error(), Ok: false},
			nil
	}
	return &types.AddUserResp{Message: "添加成功", Ok: true}, nil
}

// 私有方法
// 验证用户输入
func validInputUser(req *types.AddUserReq) (bool, error) {
	// 判断用户名是否合法
	if validator.IsEmptyString(req.UserName) {
		return false, errors.New("用户名不能为空")
	}

	// 判断判断手机号是否合法
	if !validator.IsChineseMobile(req.Mobile) {
		return false, errors.New("手机号不合法")
	}

	// 判断邮箱是否合法
	if !validator.IsEmail(req.Email) {
		return false, errors.New("邮箱不合法")
	}

	// 用户名是否合法
	if validator.IsEmptyString(req.FullName) {
		return false, errors.New("用户名不能为空")
	}

	// 判断密码是否合法
	if validator.IsEmptyString(req.Password) || !validator.IsStrongPassword(req.Password, 8) {
		return false, errors.New("密码不合法,密码长度大于8位,包含小写字母、大写字母、数字、特殊符号")
	}

	return true, nil
}

// GeneratePasswordBySalt 生成密码加盐
// 返回加密后的密码和盐
func GeneratePasswordBySalt(reqPassword string) (pass string, salt string) {
	salt = encryption.GenerateSalt()
	pass = encryption.Md5WithSalt(reqPassword, salt)
	return pass, salt
}

// 构建保存实体
func mappingUser(req *types.AddUserReq) *users.Users {
	// 密码加密处理
	pass, salt := GeneratePasswordBySalt(req.Password)
	req.Password = pass
	// 生成用户ID
	userId := toolbox.GetUniqueId()

	// 创建时间戳
	createdTimestamp := datetime.GetNowTimestamp()

	// 构建用户注册实体参数
	userEntity := &users.Users{
		UserName:     req.UserName,
		FullName:     sql.NullString{String: req.FullName, Valid: !validator.IsEmptyString(req.FullName)},
		Email:        req.Email,
		Mobile:       req.Mobile,
		Password:     req.Password,
		PasswordSalt: salt,
		Status:       1,
		TenantID:     sql.NullInt64{Valid: false},
		ID:           userId,
		CreatedAt:    createdTimestamp,
		UpdatedAt:    sql.NullInt64{Int64: createdTimestamp, Valid: true},
	}
	return userEntity
}

// 构建租户实体
func mappingTenant(req *types.AddUserReq) *tenants.Tenants {
	tenantId := toolbox.GetUniqueId()
	createdTimestamp := datetime.GetNowTimestamp()
	// 默认过期时间为一年
	expTimestamp := datetime.ToTimestamp(datetime.AddYears(time.Now(), 1))
	// 映射租户实体
	tenant := &tenants.Tenants{
		Name:              req.TenantName,
		ID:                tenantId,
		CreatedDate:       createdTimestamp,
		ModifiedDate:      createdTimestamp,
		TenantDomain:      req.TenantDomain,
		IsDeleted:         int64(enumeration.NotDeleted),
		Status:            int64(tenants.NotActivated),
		Type:              tenants.Personal,
		Legal:             req.FullName,
		Contact:           req.FullName,
		ContactPhone:      req.Mobile,
		Email:             sql.NullString{String: req.Email},
		RegistrationDate:  createdTimestamp,
		ExpirationDate:    expTimestamp,
		NickName:          sql.NullString{String: req.FullName},
		RegisteredAddress: "",
		Code:              "",
	}
	return tenant
}
